/*
* Copyright (C)
* Originally by: D. Dang, Texas Instruments Inc. December 2010
* Modified by: Alan Barr 2011
* Modified by: Kurt Hardin 2013
*/

#include <msp430.h>

#include "i2c.h"


static volatile char * i2cRxData;                                      // Pointer to i2cRX data
static int i2cRxByteCtr = 0;
static const char * i2cTxData;                                         // Pointer to TX data
static char i2cTxByteCtr = 0;
static char i2cStop = 1;

void i2cInit(const char address) {
	// Pin configuration
	P1OUT &= ~(BIT6 + BIT7);
	P1SEL |= (BIT6 + BIT7);                                             // Assign I2C pins to USCI_B0
	P1SEL2 |= (BIT6 + BIT7);                                            // Assign I2C pins to USCI_B0
	P1DIR &= ~(BIT6 + BIT7);											// Configure I2C pins as inputs

	// USCI configuration
	UCB0CTL1 |= UCSWRST;                                                // Enable SW reset
	UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;                               // I2C Master, synchronous mode
	UCB0CTL1 = UCSSEL_2 + UCSWRST;                                      // Use SMCLK, keep SW reset
	UCB0BR0 = 12;                                                       // fSCL = SMCLK/12 = ~100kHz
	UCB0BR1 = 0;
	UCB0I2CSA = address;
	UCB0CTL1 &= ~UCSWRST;                                               // Clear SW reset, resume operation

	// Enable interrupts
	UCB0I2CIE |= UCNACKIE;
	IE2 |= UCB0TXIE + UCB0RXIE;                                         // Enable TX and RX interrupts
}

void i2cTransmit(const char * data, const int numberOfBytes) {
    i2cTxData = data;                                                   // TX array start address
    i2cTxByteCtr = numberOfBytes;                                       // Load TX byte counter
    UCB0CTL1 |= UCTR + UCTXSTT;                                         // I2C TX, start condition
    __bis_SR_register(LPM0_bits + GIE);                                 // Enter LPM0 w/ interrupts
}

void i2cReceiveFrom(const char address, volatile char * data, const int numberOfBytes) {
	i2cStop = 0;
	i2cTransmit(&address, 1);
	i2cStop = 1;
	i2cReceive(data, numberOfBytes);
}

void i2cReceive(volatile char * data, const int numberOfBytes) {
    i2cRxData = data;                                                   // Start of i2cRX buffer
    i2cRxByteCtr = numberOfBytes;                                       // Load i2cRX byte counter
    UCB0CTL1 &= ~UCTR;													// Disable TX
    UCB0CTL1 |= UCTXSTT;                                                // I2C start condition
    __bis_SR_register(LPM0_bits + GIE);                                 // Enter LPM0 w/ interrupts
}

char i2cNackHandler(void) {
	char lpmToExit = 0;
	UCB0CTL1 |= UCTXSTP;
	while(UCB0CTL1 & UCTXSTP);
	while(1);						// FIXME Handle no acknowledge from slave
    return lpmToExit;
}

char i2cRxHandler(void) {
	char lpmToExit = 0;
	if (i2cRxByteCtr) {
		*i2cRxData++ = UCB0RXBUF;                                       // Move i2cRX data to address i2cRxData
		if (i2cStop & (i2cRxByteCtr == 1)) {
			UCB0CTL1 |= UCTXSTP;                                  		// I2C stop condition
		}
		i2cRxByteCtr--;                                    				// Decrement i2cRX byte counter
	}
	else {
		*i2cRxData++ = UCB0RXBUF;                                       // Move final i2cRX data to i2cRxData
		lpmToExit = LPM0_bits;
	}
	return lpmToExit;
}

char i2cTxHandler(void) {
	char lpmToExit = 0;
	if (i2cTxByteCtr) {                                                 // Check TX byte counter
		UCB0TXBUF = *i2cTxData++;                                       // Load TX buffer
		i2cTxByteCtr--;                                                 // Decrement TX byte counter
	}
	else {
		if (i2cStop) {
			UCB0CTL1 |= UCTXSTP;                                        // I2C stop condition
		}
		IFG2 &= ~UCB0TXIFG;
		lpmToExit = LPM0_bits;			                                // Exit LPM0
	}
	return lpmToExit;
}

    
